package gu.sql2java.transformer;

import static com.google.common.base.Preconditions.checkArgument;

import java.lang.reflect.Method;

import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
import com.google.common.base.Throwables;
import com.google.common.reflect.TypeToken;

/**
 * 类型转换器工作上下文
 * @author guyadong
 * @since 4.4.0
 */
public class TransformerContext implements ContextProvider {
	public static final TransformerContext DEFAULT = new TransformerContext();
	private final boolean overriden;

	public TransformerContext() {
		this.overriden = isOverrideMethod(getClass(), "placeholderOnNull", BaseTypeTransformer.class, TypeToken.class)
				|| isOverrideMethod(getClass(), "placeholderOnEmpty", BaseTypeTransformer.class, TypeToken.class)
				|| isOverrideMethod(getClass(), "elementFilter", BaseTypeTransformer.class, TypeToken.class)
				|| isOverrideMethod(getClass(), "functionOnTransformError", BaseTypeTransformer.class, TypeToken.class);
	}
	/**
	 * 获取指定类型转换器的 {@link Supplier}，用于获取当值为空时的占位符，占位符的值封装在 {@link Supplier} 中<br>
	 * 注意：如果子类未重写此方法，则返回 {@code null}，代表无效占位符
	 * @param <T>
	 * @param typeTransformer
	 * @param valueType
	 */
	public <T> Supplier<T> placeholderOnNull(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
		return null;
	}
	/**
	 * 获取指定类型转换器的 {@link Supplier}，用于获取当值为空时的占位符，占位符的值封装在 {@link Supplier} 中<br>
	 * 注意：如果子类未重写此方法，则返回 {@code null}，代表无效占位符
	 * @param <T>
	 * @param typeTransformer
	 * @param valueType
	 */
	public <T> Supplier<T> placeholderOnEmpty(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
		return null;
	}
	/**
	 * 获取指定元素类型的容器元素过滤器<br>
	 * 注意：如果子类未重写此方法，则返回 {@code null}，代表无效过滤器
	 * @param <T>
	 * @param typeTransformer
	 * @param elementType
	 */
	public <T,E> Predicate<T> elementFilter(BaseTypeTransformer typeTransformer, TypeToken<E> elementType) {
		return null;
	}

	public <T,E extends Throwable> OnTransformError<T,E> functionOnTransformError(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
		return null;
	}
	/**
	 * 构建本地上下文，用于存储 {@link TransformerContext} 对象中的
	 * {@link #placeholderOnNull(BaseTypeTransformer, TypeToken)},
	 * {@link #placeholderOnEmpty(BaseTypeTransformer, TypeToken)},
	 * {@link #elementFilter(BaseTypeTransformer, TypeToken)} 三个方法的返回值。
	 * 
	 * @param typeTransformer 类型转换器
	 * @param valueType       值类型
	 * @param elementType     元素类型, 对于非容器类型，元素类型可为 {@code null}，对于容器类型，元素类型不可为
	 *                        {@code null}，否则无法实现元素过滤。
	 * @return 本地上下文
	 */
	public final LocalContext buildLocalContext(BaseTypeTransformer typeTransformer, TypeToken<?> valueType,
			TypeToken<?> elementType) {
		if(overriden){
			checkArgument(null != typeTransformer, "typeTransformer cannot be null");
			checkArgument(null != valueType, "valueType cannot be null");
			Supplier<?> placeholderOnNull = placeholderOnNull(typeTransformer, valueType);
			Supplier<?> placeholderOnEmpty = placeholderOnEmpty(typeTransformer, valueType);
			Predicate<?> elementFilter = null == elementType ? null : elementFilter(typeTransformer, elementType);
			OnTransformError<?,?> onTransformError = functionOnTransformError(typeTransformer, valueType);
			return new LocalContext(placeholderOnNull, placeholderOnEmpty, elementFilter, onTransformError);
		}
		return LocalContext.NULL_CONTEXT;
	}
	@Override
	public TransformerContext getContext() {
		return this;
	}

	/**
	 * 当前类是否重写了 {@link TransformerContext} 声明的方法
	 */
	final boolean isOverriden() {
		return overriden;
	}

	/**
	 * 判断指定的类是否重写了 {@link TransformerContext} 声明的指定的方法，输入参数为{@code null}或空则返回{@code false}
	 */
	static boolean isOverrideMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
		try {
			if (clazz == null || methodName == null || parameterTypes == null || parameterTypes.length == 0) {
				return false;
			}
			if (!TransformerContext.class.isAssignableFrom(clazz)) {
				return false;
			}
			Method method = clazz.getMethod(methodName, parameterTypes);
			return method.getDeclaringClass() != TransformerContext.class;
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 用于处理 {@link TransformerContext} 对象转换过程中出现的异常的函数式接口
	 * 
	 * @param <T> 输出类型
	 * @param <E> 异常类型
	 */
	public static interface OnTransformError<T, E extends Throwable> {
		/**
		 * 处理转换过程中出现的异常
		 * 
		 * @param f 转换前的值
		 * @param e 转换过程中出现的异常
		 * @return 转换后的值
		 * @throws E 转换过程中出现的异常
		 */
		<F>T onThrow(F f, E e) throws E;
	}
	/**
	 * 本地上下文，用于预先存储 {@link TransformerContext} 对象中的
	 * {@link #placeholderOnNull(BaseTypeTransformer, TypeToken)},
	 * {@link #placeholderOnEmpty(BaseTypeTransformer, TypeToken)},
	 * {@link #elementFilter(BaseTypeTransformer, TypeToken)} 三个方法的返回值。
	 * 用于调用者从 {@link ContextContainer} 对象快速获取特定类型的上下文数据。而不必每次都通过遍历容器中所有 {@link ContextProvider} 对象得到。
	 * 
	 */
	public static final class LocalContext {
		public static final LocalContext NULL_CONTEXT = new LocalContext(null, null, null, null);
		private final Supplier<?> placeholderOnNull;
		private final Supplier<?> placeholderOnEmpty;
		private final Predicate<?> elementFilter;
		private final OnTransformError<?,?>  onTransformError;
		private final boolean allNull;
	
		private LocalContext(Supplier<?> placeholderOnNull, Supplier<?> placeholderOnEmpty,
				Predicate<?> elementFilter, OnTransformError<?,?> onTransformError) {
			this.placeholderOnNull = placeholderOnNull;
			this.placeholderOnEmpty = placeholderOnEmpty;
			this.elementFilter = elementFilter;
			this.onTransformError = onTransformError;
			this.allNull = placeholderOnNull == null && placeholderOnEmpty == null && elementFilter == null && onTransformError == null;
		}
		/**
		 * 是否为所有字段全为 {@code null} 的本地上下文
		 */
		public boolean isNull() {
			return allNull;
		}
		/**
		 * 如果 {@link #placeholderOnNull} 为 {@code null}，则返回 {@code null}，否则返回
		 * {@link #placeholderOnNull} 的返回值。
		 * 
		 * @return {@code null} 或 {@link #placeholderOnNull} 的返回值
		 */
		@SuppressWarnings("unchecked")
		public <T> T nullOrPlaceholderOnNull() {
			return placeholderOnNull == null ? null : (T) placeholderOnNull.get();
		}
	
		/**
		 * 如果 {@link #placeholderOnEmpty} 为 {@code null}，则返回 {@code empty}，否则返回
		 * {@link #placeholderOnEmpty} 的返回值。
		 * 
		 * @param empty 空值
		 * @return 空值或 {@link #placeholderOnEmpty} 的返回值
		 */
		@SuppressWarnings("unchecked")
		public <T> T emptyOrPlaceholderOnEmpty(T empty) {
			return placeholderOnEmpty == null ? empty : (T) placeholderOnEmpty.get();
		}
	
		@SuppressWarnings("unchecked")
		public <T> Predicate<T> getElementFilter() {
			return (Predicate<T>) elementFilter;
		}
		/**
		 * 处理转换过程中出现的异常<br>
		 * 如果没有定义异常处理器( {@link #onTransformError} 为 {@code null} )则抛出异常
		 * 
		 * @param <F> 输入类型
		 * @param <T> 输出类型
		 * @param <E> 异常类型
		 * @param input 输入值
		 * @param e     转换过程中出现的异常
		 * @return 输出值
		 * @throws E 转换过程中出现的异常
		 */
		@SuppressWarnings("unchecked")
		public <F, T, E extends Throwable> T onTransformError(F input, E e) {
			Throwable err = e;
			if (null != onTransformError) {
				try {
					return (T) ((OnTransformError<T, E>) onTransformError).onThrow(input, e);
				} catch (Throwable ex) {
					err = ex;
				}
			}
			Throwables.throwIfUnchecked(err);
			throw new RuntimeException(err);
		}
	}

}